using System.Collections;
using SimpleJson;

using System;
using System.Text;
using System.Net;
using System.Net.Sockets;
using System.Collections.Generic;
using UnityEngine;
using BestHTTP.WebSocket;

namespace Mars.Client
{
	public class MarsClient : IDisposable
	{
		public const string EVENT_DISCONNECT = "disconnect";
		
		private EventManager eventManager;
		private Protocol protocol;
		private bool disposed = false;
		private uint reqId = 1;
		public WebSocket webSocket;
		public string ws_address;

		public string _host;
		public int _port;

		
		public  MarsClient( string host , int port ){

			_host = host;
			_port = port;
			
		
			this.eventManager = new EventManager();

			if ( webSocket == null )
			{

				ws_address = string.Format("ws://{0}:{1}/",host,port.ToString());

				webSocket = new WebSocket( new Uri(ws_address) );
				webSocket.OnOpen += OnOpen;
				webSocket.OnMessage += OnMessageReceived;
				webSocket.OnClosed += OnClosed;
				webSocket.OnError += OnError;

				webSocket.OnIncompleteFrame  += OnIncompleteFrame;

				this.protocol = new Protocol(this, webSocket);
				Debug.Log ( " Start WebSocket Init "  +  ws_address  );
			}
		}

		public void init(){

			try {
				webSocket.Open();
			}
			catch (Exception e) {
				Console.WriteLine(String.Format("unable to connect to server: {0}", e.ToString()));
				return;
			}
		}
		
		public void Close( ushort code  , string message ) {
			if (webSocket != null && webSocket.IsOpen) {
				Debug.Log ("Sending message...\n");
				webSocket.Close ( code , message );
			}
		}
	
		public void Send( byte[] byte_message  ){
			Debug.Log ("Start byte send .....");
			if (webSocket != null && webSocket.IsOpen) {
				Debug.Log ("Sending message...\n");
				webSocket.Send (byte_message);
			} else {
				Debug.Log ("Can not send byte_message may be websocket not opened \n");
			}
		}
		public void Send( string message = "" ){
			Debug.Log ("msg is " + message);
			if (webSocket != null && webSocket.IsOpen) {
				Debug.Log ("Sending message...\n");
				webSocket.Send (message);
			} else {
				Debug.Log ("Can not send message may be websocket not opened \n");
			}
		}

		private void OnIncompleteFrame(WebSocket webSocket, BestHTTP.WebSocket.Frames.WebSocketFrameReader reader) {
			Debug.Log("OnIncompleteFrame: " + reader.Data.Length );
		}

		private void OnBinaryMessageReceived(WebSocket webSocket, byte[] message) {
			Debug.Log("Binary Message received from server. Length: " + message.Length);
		}
		// Called when the web socket is open, and we are ready to send and receive data
		void OnOpen(WebSocket ws)
		{
			Debug.Log ("-websocket is opened " + webSocket.IsOpen.ToString());
		}
	
		// Called when we received a text message from the server
		void OnMessageReceived(WebSocket ws, string message)
		{
			Debug.Log(string.Format("-Message received: {0}\n", message));

		}
	
		// Called when the web socket closed
		void OnClosed(WebSocket ws, UInt16 code, string message)
		{
			Debug.Log(string.Format("-WebSocket closed! Code: {0} Message: {1}\n", code, message));
			webSocket = null;
		}
		
		// Called when an error occured on client side
		void OnError(WebSocket ws, Exception ex)
		{
			string errorMsg = string.Empty;
			if (ws.InternalRequest.Response != null)
				errorMsg = string.Format("Status Code from Server: {0} and Message: {1}", ws.InternalRequest.Response.StatusCode, ws.InternalRequest.Response.Message);
			
			Debug.Log(string.Format("-An error occured: {0}\n", (ex != null ? ex.Message : "Unknown Error " + errorMsg)));
			webSocket = null;
		}
		
		public void connect(){
			protocol.start(null, null);
		}
		
		public void connect(JsonObject user){
			protocol.start(user, null);
		}
		
		public void connect(Action<JsonObject> handshakeCallback){
			protocol.start(null, handshakeCallback);
		}
		
		public bool connect(JsonObject user, Action<JsonObject> handshakeCallback){
			try{
				Debug.Log(" start connect to server ");
				protocol.start(user, handshakeCallback);
				return true;
			}catch(Exception e){
				Debug.Log(e.ToString());
				return false;
			}
		}
		
		public void request(string route, Action<JsonObject> action){
			this.request(route, new JsonObject(), action);
		}
		
		public void request(string route, JsonObject msg, Action<JsonObject> action){
			this.eventManager.AddCallBack(reqId, action);
			protocol.send (route, reqId, msg);
			
			reqId++;
		}


		public void notify(string route, JsonObject msg){
			protocol.send (route, msg);
		}
		
		public void on(string eventName, Action<JsonObject> action){
			eventManager.AddOnEvent(eventName, action);
		}
		
		internal void processMessage(Message msg){
			if(msg.type == MessageType.MSG_RESPONSE){
				eventManager.InvokeCallBack(msg.id, msg.data);
			}else if(msg.type == MessageType.MSG_PUSH){
				Debug.Log(" msg type is msg push besthttp!");
				eventManager.InvokeOnEvent(msg.route, msg.data);
			}
		}
		
		public void disconnect(){
			//Debug.Log(" call Mars Client disconnect ");
			Dispose ();
		}
		
		public void Dispose() {
			//Debug.Log(" call Mars Client Dispose ");
			Dispose(true);
			GC.SuppressFinalize(this);
		}
		
		// The bulk of the clean-up code 
		protected virtual void Dispose(bool disposing) {

			//Debug.Log(" call Mars Client Dispose protected ");

			if(this.disposed) return;
			
			if (disposing) {

				//Debug.Log(" call Mars Client dispose ");
				// free managed resources
				this.protocol.close();
				this.webSocket.Close();
				this.disposed = true;
				
				//Call disconnect callback
				eventManager.InvokeOnEvent(EVENT_DISCONNECT, null);
			}
		}

	}
}